package win.bitterwine.redis;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.util.MapUtil;
import org.smart4j.framework.util.StringUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import win.bitterwine.entity.B_admin;
import win.bitterwine.entity.B_module;
import win.bitterwine.util.PublicTool;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * Created by wqy on 2017/6/20.
 */
public class RedisTool {
    private static final Logger logger = LoggerFactory.getLogger(RedisTool.class);
    private JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();

    /**
     * 根据key删除内容
     */
    public void delete(String key){
        Jedis jedis = jedisPool.getResource();
        Set<String> keys = jedis.keys(key);
        for(String ss : keys){
            jedis.del(ss);
        }
        JedisPoolUtil.release(jedisPool, jedis);
    }


    /**
     * 获取key的类型
     * @param key
     * @return
     */
    public String getKeyType(String key){
        String type = "";
        Jedis jedis = jedisPool.getResource();
        type = jedis.type(key);
        JedisPoolUtil.release(jedisPool, jedis);
        return type;
    }

    /**
     * 根据key获取hash类型的redis内存数据
     * @return
     */
    public Map<String, String> getHash(String key){
        Map<String, String> hash = null;
        Jedis jedis = jedisPool.getResource();
        hash = jedis.hgetAll(key);
        JedisPoolUtil.release(jedisPool, jedis);
        return hash;
    }

    public boolean isExists(String key) {
        Jedis jedis = jedisPool.getResource();
        Boolean exists = jedis.exists(key);
        JedisPoolUtil.release(jedisPool, jedis);
        return exists;
    }

    /**
     * 存储一条记录到redis中
     *
     * @param key
     * @param value
     * @param flag       如果存在该key true 覆盖 false 不覆盖
     * @param expireTime 设置key的过期时间,如不设置则 输入 0
     * @return
     * @throws Exception
     * @author wqy
     * @date 2017年3月22日 上午10:43:04
     */
    public boolean setValue(String key, String value, boolean flag,
                            int expireTime) throws Exception {
        Jedis jedis = jedisPool.getResource();
        if (jedis.exists(key) && flag == false) {
            JedisPoolUtil.release(jedisPool, jedis);
            return true;
        }
        String ss = jedis.set(key, value);
        if (expireTime != 0) {
            jedis.expire(key, expireTime);
        }
        JedisPoolUtil.release(jedisPool, jedis);
        if (ss == null) {
            return false;
        }
        return true;
    }

    /**
     * redis中获取一条普通的记录
     */
    public String getValue(String key){
        Jedis jedis = jedisPool.getResource();
        if(!jedis.exists(key)){
            return "";
        }
        String str = jedis.get(key);
        JedisPoolUtil.release(jedisPool, jedis);
        return str;
    }

    /**
     * 根据查询条件生成redis中存储的key
     * @param clazz  查询表对应的class
     * @param fieldMap  获取的参数
     * @param sort    排序方式,正序的时候可设置为null
     * @param other   其他参数的参数名和值的字符串 (例如 id=1  要写成 id1)
     * @return
     */
    public static String getRedisKey(Class<?> clazz, Map<String,Object> fieldMap, String sort, String other){
        String key = PublicTool.getsubTableName(clazz.getName());
        if (MapUtil.isNotEmpty(fieldMap)) {
            Set<String> keySet = fieldMap.keySet();
            List<String> list = new ArrayList<String>(keySet);
            Collections.sort(list); // 保证生成的key一致
            for (String string : list) {
                key += string + fieldMap.get(string);
            }
        }
        if(StringUtil.isNotEmpty(sort))
            key += sort;
        if(StringUtil.isNotEmpty(other))
            key += sort;
        return key;
    }

    /**
     * 将表的数据存储到redis 对象保存存对象id
     *
     * @throws Exception
     * @author wqy
     * @date 2017年3月20日 上午11:31:13
     * @isCover 单个对象存在时是否覆盖(只在修改对象的时候, 设置为true)
     */
    public List<String> setTableToRedis(List<Object> list, boolean isCover) throws Exception {
        if (list == null || list.isEmpty()) {
            System.out.println("list数据为空,setTableToRedis");
            return null;
        }
        Jedis jedis = jedisPool.getResource();
        Class<?> clazz = list.get(0).getClass();
        String name = PublicTool.getsubTableName(clazz.getName());
        Field[] fields = clazz.getDeclaredFields(); // 字段
        List<String> getMethods = PublicTool.getGetMeth(clazz);
        List<String> rightField = PublicTool.getRightField(fields); // 该对象的所有bean
        List<String> keyNames = new ArrayList<String>();
        for (Object obj : list) { // 数据库查到的对象的集合
            Method getIdMethod = clazz.getDeclaredMethod("getId", null);
            Object id = getIdMethod.invoke(obj, null); // 主键

            Map<String, String> hash = putFiledMap(getMethods, clazz, obj, rightField);
            // key名字,以当前 对象名_id 格式存储 原来有值则不覆盖
            redisSet(name + "_" + id, hash, isCover);
            keyNames.add(name + "_" + id);
        }
        JedisPoolUtil.release(jedisPool, jedis);
        return keyNames;
    }



    /**
     * 执行一个对象的get方法,获取值,封装一个map
     *
     * @param getMethods
     * @param clazz
     * @param object
     * @param rightField
     */
    public Map<String, String> putFiledMap(List<String> getMethods, Class<?> clazz, Object object, List<String> rightField)
            throws Exception {
        Map<String, String> hash = new HashMap<String, String>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (int i = 0; i < getMethods.size(); i++) { // 执行每一个方法,获取对应的值
            Method m = clazz.getDeclaredMethod(getMethods.get(i), null);
            Class<?> returnType = m.getReturnType(); //返回值类型
            String returenTypeName = returnType.getName();
            Object result = m.invoke(object, null);
            String fieldName = rightField.get(i);
            // 如果是时间类型,以固定格式显示
            if (returenTypeName.contains("Date")) {
                if (result == null) {
                    hash.put(fieldName, sdf.format(new Date()));
                    continue;
                }
                String time = sdf.format((Date) result);
                hash.put(fieldName, time);
            } else if (returenTypeName.contains("Integer") || returenTypeName.contains("int")
                    || returenTypeName.contains("Long") || returenTypeName.contains("long")) {
                if (result == null) {
                    hash.put(fieldName, String.valueOf(0));
                    continue;
                }
                hash.put(fieldName, result.toString());
            } else {
                if (result == null) {
                    hash.put(fieldName, "");
                    continue;
                }
                hash.put(fieldName, result.toString());
            }
        }
        return hash;
    }

    /**
     * jedis hmset 方法的封装
     *
     * @param flag true 直接覆盖原值, false 如果原来有值,则不覆盖
     * @param key  键
     * @param hash map集合
     */
    public void redisSet(String key, Map<String, String> hash, boolean flag) {
        Jedis jedis = jedisPool.getResource();
        if (key == "" || key == null || hash == null || hash.isEmpty()) {
            return;
        }
        if (flag == true || (flag == false && !jedis.exists(key))) {
            jedis.hmset(key, hash);
        }
        JedisPoolUtil.release(jedisPool, jedis);
    }

    /**
     * 通过clazz 对象和 id 在redis中获得单个对象
     * @param clazz
     * @param id
     * @throws Exception
     * @author wqy
     * @date 2017年3月21日 下午1:48:33
     */
    public Object getRedisObjectById(Class<?> clazz, long id) throws Exception {
        JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
        Jedis jedis = jedisPool.getResource();
        String className = clazz.getName();
        String name = className.substring(className.lastIndexOf("_") + 1);
        List<String> rightField = PublicTool.getRightField(clazz.getDeclaredFields());
        List<String> setMethList = PublicTool.getSetMeth(clazz);
        Map<String, String> hgetAll = jedis.hgetAll(name + "_" + id);
        if (hgetAll.isEmpty()) {
            return null;
        }
        Object newInstance = clazz.newInstance(); // 创建一个新对象,封装bean
        for (int i = 0; i < rightField.size(); i++) {
            String str = hgetAll.get(rightField.get(i));
            String[] val = setMethList.get(i).split("@");
            Method m = clazz.getDeclaredMethod(val[0], new Class[]{PublicTool.getMultiClass(val[1])});
            gotoInvoke(val, str, newInstance, m);
        }
        JedisPoolUtil.release(jedisPool, jedis);
        return newInstance;
    }

    /**
     * 利用反射,将一个对象保存到redis中
     */
    public void saveOneToRedis(Object object, long id, boolean flag) throws Exception {
        if (object == null || id <= 0) {
            System.out.println("对象不合法,saveOneToRedis");
            return;
        }
        Class<?> clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields(); // 字段
        List<String> getMethods = PublicTool.getGetMeth(clazz);
        List<String> rightField = PublicTool.getRightField(fields); // 该对象的所有bean
        Jedis jedis = jedisPool.getResource();
        String name = PublicTool.getsubTableName(clazz.getName());
        String beanName = "B_"+name;
        Map<String, String> hash = putFiledMap(getMethods, clazz, object, rightField);
        // key名字,以当前 对象名_id 格式存储
        jedis.hmset(name + "_" + id, hash);

        updateSave(clazz, jedis, name, beanName, id, flag);

        JedisPoolUtil.release(jedisPool, jedis);
    }

    /**
     * save 时更新缓存
     * @param flag  为true的时候更新 getAll相关的key
     */
    public void updateSave(Class<?> clazz, Jedis jedis, String name, String beanName, long id, boolean flag){
        //更新getAll缓存中的内容
        String key_one = PublicTool.getCacheSelectListKey(clazz, "", "");
        String key_two = PublicTool.getCacheSelectListKey(clazz, "", "id desc");
        if(flag){
            List<String> lrange_one = jedis.lrange(key_one, 0, -1);
            List<String> lrange_two = jedis.lrange(key_two, 0, -1);
            if (CollectionUtils.isNotEmpty(lrange_one)) {
                jedis.rpush(key_one, name + "_" + id);
            }
            if (CollectionUtils.isNotEmpty(lrange_two)) {
                jedis.rpush(key_two, name + "_" + id);
            }
        }

        //更新该表的相关 非 getAll 缓存
        Set<String> keys = jedis.keys(beanName + "*");
        for(String key : keys){
            //删除不是getAll的key
            if(!key.equals(key_one) && !key.equals(key_two)){
                jedis.del(key);
            }
        }
    }



    /**
     * 根据key获取 list数据
     *
     * @param key
     * @return
     * @throws Exception
     */
    public List<Object> getListValue(String key) throws Exception {
        Jedis jedis = jedisPool.getResource();
        List<String> lrange = jedis.lrange(key, 0, -1);
        List<Object> list = new ArrayList<Object>();
        for (String lra : lrange) {
            String[] split = lra.split("_");
            Class<?> clazz = Class.forName("win.bitterwine.entity.B_" + split[0]);
            Object object = getRedisObjectById(clazz, Integer.parseInt(split[1]));
            list.add(object);
        }
        JedisPoolUtil.release(jedisPool, jedis);
        return list;
    }

    /**
     * 将list 存入 redis
     *
     * @param key
     * @param flag       如果存在该key true 覆盖 false 不覆盖
     * @param expireTime
     * @throws Exception
     */
    public void setListValue(String key, List<String> list, boolean flag,
                             int expireTime) throws Exception {
        Jedis jedis = jedisPool.getResource();
        if (jedis.exists(key) && flag == false) {
            JedisPoolUtil.release(jedisPool, jedis);
            return;
        }
        for (String ss : list) {
            jedis.rpush(key, ss);
        }
        if (expireTime != 0) {
            jedis.expire(key, expireTime);
        }
        JedisPoolUtil.release(jedisPool, jedis);
    }

    /**
     * 执行set方法
     *
     * @param val         set方法的数组
     * @param temp        redis中获得内容
     * @param newInstance 新创建 的对象
     * @param m           Method 对象
     * @throws Exception
     * @author wqy
     * @date 2017年3月21日 下午2:48:59
     */
    public void gotoInvoke(String[] val, String temp, Object newInstance, Method m) throws Exception {
        if (temp != null && !temp.equals("null") && temp != "") {
            if (val[0].contains("Date")) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = sdf.parse(temp);
                m.invoke(newInstance, new Object[]{date});
            } else if (val[1].contains("Integer") || val[1].contains("int")) {
                Integer integer = Integer.parseInt(temp);
                m.invoke(newInstance, new Object[]{integer});
            } else if (val[1].contains("Long") || val[1].contains("long")) {
                Long lo = Long.parseLong(temp);
                m.invoke(newInstance, new Object[]{lo});
            }  else {
                m.invoke(newInstance, new Object[]{temp});
            }
        }
    }


    public static void main(String[] args) throws Exception {
        Date now = new Date();
        B_admin b_admin1 = new B_admin(1, "123456", "wqy", "18909833716",
                "1033943317@qq.com", "1033943317", "15242004780", now, 0, "将军",
                1);
        B_admin b_admin2 = new B_admin(2, "12345", "wqywqy", "18933716",
                "1943317@qq.com", "1033317", "152424780", now, 0, "将军刚刚", 2);
        B_module b_module1 = new B_module(1, "名字1", "路径1", now, 0, 1);
        B_module b_module2 = new B_module(2, "名字2", "路径2", now, 0, 2);
        B_module b_module3 = new B_module(3, "名字3", "路径3", now, 0, 1);
        B_module b_module4 = new B_module(4, "名字4", "路径4", now, 0, 2);

        List<Object> list = new ArrayList<Object>();
         list.add(b_module1);
         list.add(b_module2);
         list.add(b_module3);
         list.add(b_module4);

        //list.add(b_admin1);
        //list.add(b_admin2);
        RedisTool redisTool = new RedisTool();
//        List<String> strings = redisTool.setTableToRedis(list, true);
//        System.out.println(strings);

//        B_admin admin1 = (B_admin) redisTool.getRedisObjectById(B_admin.class, 1);
//        System.out.println(admin1);

        //B_module module1 = (B_module) redisTool.getRedisObjectById(B_module.class,1);
        //System.out.println(module1);

        System.out.println(redisTool.getListValue("B_rolesid desc"));


    }

}
